using System;
using System.Diagnostics;
using System.Drawing;
using Microsoft.Xna.Framework;
using Trippy.Animation;
using Trippy.Levels;
using Trippy.CollidableGameComponents.Agents;

namespace Trippy.CollidableGameComponents
{
    public class CollidableGameComponent : DrawableTrippyComponent
    {
        public enum CollisionSide { Top, Right, Bottom, Left }

        public float Elasticity { get; set; }
        public float Friction { get; set; }

        public void SetRightX(float value)
        {
            Position = new Vector2(value - BoundingBox.Width / 2f, Position.Y);
        }

        public void SetLeftX(float value)
        {
            Position = new Vector2(value + BoundingBox.Width / 2f, Position.Y); 
        }

        public void SetBottomY(float value)
        {
            Position = new Vector2(Position.X, value - BoundingBox.Height / 2f);
        }

        public void SetTopY(float value)
        {
            Position = new Vector2(Position.X, value + BoundingBox.Height / 2f);
        }

        public CollidableGameComponent(Level level)
            : base(level.Game)
        {
            CurrentLevel = level;
            Friction = 1.0f;
        }



        /// <summary>
        /// Determines if two CollidableGameComponents collide. If collision is detected, CollisionHandler functions are called.
        /// </summary>
        /// <param name="component"></param>
        public void DetectCollisions(CollidableGameComponent component)
        {
            Debug.Assert(component != null, "Input component should not be null");
            Debug.Assert(Sprite != null || Sprite != null, "Sprite is null");
            Debug.Assert(component.Sprite != null || component.Sprite != null, "Sprite is null");

            // Determine the bounding box based on location, sprite size, and scale factor
            var collidingSpriteRegion = new RectangleF(component.Position.X - component.BoundingBox.Width / 2f, component.Position.Y - component.BoundingBox.Height / 2f, component.BoundingBox.Width, component.BoundingBox.Height);

            // Create four 1px bounding boxes to represent edges of region
            var topEdge = new RectangleF(Position.X - BoundingBox.Width / 2.0f, Position.Y - 1 - BoundingBox.Height / 2.0f, BoundingBox.Width, 1);
            var rightEdge = new RectangleF(Position.X + BoundingBox.Width / 2.0f, Position.Y - BoundingBox.Height / 2.0f, 1, BoundingBox.Height);
            var leftEdge = new RectangleF(Position.X - 1 - BoundingBox.Width / 2.0f, Position.Y - BoundingBox.Height / 2.0f, 1, BoundingBox.Height);
            var bottomEdge = new RectangleF(Position.X - BoundingBox.Width / 2.0f, Position.Y + BoundingBox.Height / 2.0f, BoundingBox.Width, 1);

            // Determine which of those edges, if any, intersect with the region we are testing
            topEdge = RectangleF.Intersect(topEdge, collidingSpriteRegion);
            rightEdge = RectangleF.Intersect(rightEdge, collidingSpriteRegion);
            leftEdge = RectangleF.Intersect(leftEdge, collidingSpriteRegion);
            bottomEdge = RectangleF.Intersect(bottomEdge, collidingSpriteRegion);

            // Determine which edge intersects the most
            float side = Math.Max(Math.Max(topEdge.Width, bottomEdge.Width),Math.Max(leftEdge.Height, rightEdge.Height));

            // If none of them intersect, nothing happens
            if (side > 2)
            {
                // Otherwise, find out which edge collided and call the collision handlers of each CollidableGameComponent

                CollisionSide selfSide;
                CollisionSide colliderSide;
                if (side == topEdge.Width)
                {
                    // Our top edge, colliding sprite's bottom edge
                    selfSide = CollisionSide.Top;
                    colliderSide = CollisionSide.Bottom;
                }
                else if (side == bottomEdge.Width)
                {
                    // Our bottom edge, colliding sprite's top edge
                    selfSide = CollisionSide.Bottom;
                    colliderSide = CollisionSide.Top;
                }
                else if (side == leftEdge.Height)
                {
                    selfSide = CollisionSide.Left;
                    colliderSide = CollisionSide.Left;
                }
                else// if (side == rightEdge.Height)
                {
                    selfSide = CollisionSide.Right;
                    colliderSide = CollisionSide.Right;
                }
                // Call the collision handlers on the two objects
                if (!(component is Hero
                    && (component as Hero).CurrentAbility != null
                    && (component as Hero).CurrentAbility.OnCollide(this, colliderSide)
                    || (this is Hero
                    && (this as Hero).CurrentAbility != null
                    && (this as Hero).CurrentAbility.OnCollide(component, selfSide)))) 
                    //i'm really sorry about this, but i need abilities to override collision some times...
                    CollisionHandler(component, selfSide);
                //component.CollisionHandler(this, colliderSide);
            }

        }

        /// <summary>
        /// Called when another object collides with the base object. 
        /// </summary>
        /// <param name="component"></param>
        /// <param name="side"></param>
        protected virtual void CollisionHandler(CollidableGameComponent component, CollisionSide side)
        {
            
        }

        public void UpdateStandardPhysics(float elapsedTime)
        {
            Velocity += Acceleration * elapsedTime;
            Velocity -= new Vector2(Velocity.X * (CurrentLevel.Friction/100f) * Friction, 0);
            Position += Velocity * elapsedTime;
        }

        public override void Update(GameTime gameTime)
        {
            BoundingBox = CalculateBoundingBox();
//             if (CurrentLevel.BlastLine(Position))
//                 OnBlastLine();
            base.Update(gameTime);
        }

        private RectangleF CalculateBoundingBox()
        {
            return new RectangleF(Position.X - CalculateWidth() / 2f, Position.Y - CalculateHeight() / 2f, CalculateWidth(), CalculateHeight());
        }

        private float CalculateWidth()
        {
            return (Sprite.Width + 2 * Sprite.HorizontalTrim) * Scale.X;
        }

        private float CalculateHeight()
        {
            return (Sprite.Height + 2 * Sprite.VerticalTrim) * Scale.Y;
        }

        public RectangleF BoundingBox { get; private set; }

        public virtual void Restore()
        {
            Position = InitialPosition;
            Velocity = new Vector2();
            Acceleration = new Vector2();
            markedForDeletion = false;
        }
    }
}
